#include "Scene/Node.h"

#include "Framework/StableHeaders.h"

namespace tyro
{
	namespace Scene
	{
		
		Framework::NameGenerator Node::nameGen("UnnamedNode");

		Node::Node( String name )
			:	name(name)
			,	parent(0)
			,	translation(Math::Vector3f::ZERO)
			,	scale(Math::Vector3f::UNIT_SCALE)
			,	rotation(Math::Quaternionf::IDENTITY)
			,	derivedTranslation(Math::Vector3f::UNIT_SCALE)
			,	derivedScale(Math::Vector3f::ZERO)
			,	derivedRotation(Math::Quaternionf::IDENTITY)
			,	transformOutOfDate(true)
			,	NeedsParentUpdate(false)
		{

		}

		Node::Node()
			:	parent(0)
			,	translation(Math::Vector3f::ZERO)
			,	scale(Math::Vector3f::UNIT_SCALE)
			,	rotation(Math::Quaternionf::IDENTITY)
			,	derivedTranslation(Math::Vector3f::UNIT_SCALE)
			,	derivedScale(Math::Vector3f::ZERO)
			,	derivedRotation(Math::Quaternionf::IDENTITY)
			,	transformOutOfDate(true)
			,	NeedsParentUpdate(false)
		{
			name = nameGen.Generate();


		}

		void Node::Update()
		{
			UpdateFromParent();

			ChildNodeMap::iterator it, itend;
			itend = children.end();
			for (it = children.begin(); it != itend; ++it)
			{
				Node* child = it->second;
				child->Update();
			}
		}

		void Node::UpdateFromParent() const
		{
			UpdateFromParentImpl();
		}

		void Node::UpdateFromParentImpl() const
		{
			if (parent)
			{
				// Update orientation
				const Math::Quaternionf& parentRotation = parent->GetDerivedRotation();
				if (true)
				{
					// Combine orientation with that of parent
					derivedRotation = parentRotation * rotation;
				}
				else
				{
					// No inheritance
					derivedRotation = rotation;
				}

				// Update scale
				const Math::Vector3f& parentScale = parent->GetDerivedScale();
				if (true)
				{
					// Scale own position by parent scale, NB just combine
					// as equivalent axes, no shearing
					derivedScale = parentScale * scale;
				}
				else
				{
					// No inheritance
					derivedScale = scale;
				}

				// Change position vector based on parent's orientation & scale
				derivedTranslation = parentRotation * (parentScale * translation);

				// Add altered position vector to parents
				derivedTranslation += parent->GetDerivedTranslation();
			} 
			else
			{
				derivedTranslation = translation;
				derivedRotation = rotation;
				derivedScale = scale;
			}
			transformOutOfDate = true;
			NeedsParentUpdate = false;
		}

		const Math::Matrix4f& Node::GetFullWorldTransform( void ) const
		{
			if (transformOutOfDate)
			{
				derivedTransform.MakeTransform(
					GetDerivedTranslation(),
					GetDerivedScale(),
					GetDerivedRotation());
				transformOutOfDate = false;
			}
			return derivedTransform;
			
		}

		void Node::SetTranslation( FLOAT_32 x, FLOAT_32 y, FLOAT_32 z )
		{
			SetTranslation(Math::Vector3f(x, y, z));
		}

		void Node::SetTranslation( Math::Vector3f vec )
		{
			translation = vec;
			FlagUpdate();
		}

		void Node::FlagUpdate()
		{
			NeedsParentUpdate  = true;
			transformOutOfDate = true;
		}

		void Node::Rotate( const Math::Vector3f axis, const Math::Radianf angle, TransformSpace relativeTo /*= TS_LOCAL*/ )
		{
			Math::Quaternionf rotation;
			rotation.FromAngleAxis(angle, axis);
			Rotate(rotation, relativeTo);
		}

		void Node::Rotate( const Math::Quaternionf& rotation, TransformSpace relativeTo /*= TS_LOCAL*/ )
		{
			// Normalise quaternion to avoid drift
			Math::Quaternionf rotationNorm = rotation;
			rotationNorm.Normalise();

			switch (relativeTo)
			{
			case TS_WORLD:
				break;
			case TS_PARENT:
				break;
			case TS_LOCAL:
				this->rotation = this->rotation * rotationNorm;
				break;
			}
			FlagUpdate();
		}

		Node* Node::CreateChildNode( const String& name, const Math::Vector3f& translation /*= Math::Vector3f::ZERO*/, const Math::Quaternionf& rotation /*= Math::Quaternionf::IDENTITY*/ )
		{
			Node* child = CreateChildImpl(name);
			child->SetTranslation(translation);
			child->Rotate(rotation);
			this->AddChild(child);
			return child;
		}

		Node* Node::CreateChildNode( const Math::Vector3f& translation /*= Math::Vector3f::ZERO*/, const Math::Quaternionf& rotation /*= Math::Quaternionf::IDENTITY*/ )
		{
			Node* child = CreateChildImpl();
			child->SetTranslation(translation);
			child->Rotate(rotation);
			this->AddChild(child);
			return child;
		}

		void Node::AddChild( Node* child )
		{
			children.insert(ChildNodeMap::value_type(child->GetName(), child));
			child->SetParent(this);
		}

		const String& Node::GetName( void ) const
		{
			
			return name;
		}

		void Node::SetParent( Node* parent )
		{
			this->parent = parent;
		}

		const Math::Vector3f& Node::GetDerivedTranslation( void ) const
		{
			if (NeedsParentUpdate)
			{
				UpdateFromParent();
			}
			return derivedTranslation;
		}

		const Math::Quaternionf& Node::GetDerivedRotation( void ) const
		{
			if (NeedsParentUpdate)
			{
				UpdateFromParent();
			}
			return derivedRotation;
		}

		const Math::Vector3f& Node::GetDerivedScale( void ) const
		{
			if (NeedsParentUpdate)
			{
				UpdateFromParent();
			}
			return derivedScale;
		}

		void Node::SetScale( FLOAT_32 x, FLOAT_32 y, FLOAT_32 z )
		{
			SetScale(Math::Vector3f(x, y, z));
		}

		void Node::SetScale( Math::Vector3f vec )
		{
			scale = vec;
			FlagUpdate();
		}


	}
}